package com.zzyl.security;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.zzyl.config.JwtTokenManagerProperties;
import com.zzyl.constant.Constants;
import com.zzyl.constant.UserCacheConstant;
import com.zzyl.utils.JwtUtil;
import com.zzyl.vo.UserAuth;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.function.Supplier;

/**
 * @author sjqn
 */
@Component
public class JwtTokenAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private JwtTokenManagerProperties jwtTokenManagerProperties;

    //spring框架提供的支持字符串通配符匹配对象
    private AntPathMatcher antPathMatcher =new AntPathMatcher();

    /**
     *授权与鉴权管理器方法重写
     */
    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext rac) {

        //1.校验token
        //获取请求对象request
        HttpServletRequest request = rac.getRequest();
        //获取请求头token
        String token = request.getHeader(Constants.USER_TOKEN);
        //非空判断，为空返回鉴权失败
        if (StrUtil.isEmpty(token)){
            return new AuthorizationDecision(false);
        }
        //解析token得到载荷userVo的json字符串
        Claims claims = JwtUtil.parseJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), token);
        String json = claims.get("currentUser").toString();

        //2.获取用户数据UserAuth并封装给springSecurity上下文，注明已认证
        //将json字符串转换为UserAuth
        UserAuth userAuth = JSONUtil.toBean(json, UserAuth.class);
        //封装已认证用户对象UsernamePasswordAuthenticationToken
        UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken
                (userAuth, userAuth.getPassword(), null);
        //封装给SpringSecurity上下文
        SecurityContextHolder.getContext().setAuthentication(upat);
        //3.鉴权操作
        //获取用户当前访问路径path
        String path = request.getRequestURI();//path="/nursing_project/list"
        //获取用户当前请求的请求方式
        String method = request.getMethod();//method="GET"
        //拼接当前访问完整路径=请求方式/资源路径
        path=method+path;//path=GET/nursing_project/list
        //从redis中获取可访问的资源路径列表List<String>：每个路径格式：请求方式/资源路径
        String json2 = redisTemplate.opsForValue().get(UserCacheConstant.ACCESS_URLS_CACHE + userAuth.getId());
        List<String> urls = JSONUtil.toList(json2, String.class);
        //遍历资源路径列表判断是否与当前访问的完整路径匹配，匹配就返回鉴权成功
        for (String url :urls){
            //url=GET/nursing_project/**
            if (antPathMatcher.match(url,path)){//antpathMatcher.match(url,path)返回true代码匹配成功，否则匹配失败
                //匹配上了，返回鉴权成功
                return new AuthorizationDecision(true);
            }
        }

        //4.来到这里说明鉴权失败
        return null;
    }
}